注册 登录
中国神经科学论坛 返回首页

thinker的个人空间 https://bbs.bioguider.com/?2 [收藏] [复制] [分享] [RSS]

日志

批量清理DEDECMS中失效的缩略图

已有 26 次阅读2026-3-25 14:21 |个人分类:网站建设

 把下述代码存成PHP文件,在网站根目录下运行。

<?php
/**
 * DedeCMS 智能缩略图管理工具(增强版)
 * 功能:
 *   1. 扫描指定ID范围内的文章
 *   2. 检测缩略图是否有效(三重检测)
 *   3. 如果缩略图失效或为空,自动从正文提取第一张有效图片作为缩略图
 *   4. 如果正文中没有有效图片,则清空缩略图字段(删除失效地址)
 *   5. 支持预览模式和执行模式
 */

require_once(dirname(__FILE__) . "/include/common.inc.php");

// 绕过Dede安全检查
if (!isset($cfg_db_type)) {
    $GLOBALS['safe_check'] = false;
}

// 获取参数
$startid = isset($_GET['startid']) ? intval($_GET['startid']) : 0;
$endid   = isset($_GET['endid']) ? intval($_GET['endid']) : 0;
$step    = isset($_GET['step']) ? intval($_GET['step']) : 50;
$timeout = isset($_GET['timeout']) ? intval($_GET['timeout']) : 5;
$min_size = isset($_GET['min_size']) ? intval($_GET['min_size']) : 256;
$mode    = isset($_GET['mode']) ? $_GET['mode'] : 'preview'; // preview / execute

// HTML界面
echo "<!DOCTYPE html><html><head><meta charset='utf-8'><title>智能缩略图管理工具</title>";
echo "<style>
body{font-family:system-ui,sans-serif;padding:20px;max-width:1200px;margin:0 auto;background:#f5f7fb;}
.toolbar{background:#fff;padding:15px 20px;border-radius:12px;box-shadow:0 1px 3px rgba(0,0,0,0.05);margin-bottom:20px;}
.log{background:#fff;border-radius:12px;padding:20px;box-shadow:0 1px 3px rgba(0,0,0,0.05);max-height:600px;overflow-y:auto;}
.log-item{font-family:monospace;font-size:13px;padding:8px 0;border-bottom:1px solid #eee;}
.success{color:#2e7d32;} .error{color:#c62828;} .warning{color:#ed6c02;} .info{color:#0d47a1;} .generated{color:#7b1fa2;} .cleared{color:#f57c00;}
button, input[type='submit']{background:#0f3b5c;color:white;border:none;padding:8px 18px;border-radius:6px;cursor:pointer;}
button:hover{background:#1e4a6e;} input[type='number']{padding:6px 10px;border:1px solid #ccc;border-radius:6px;width:100px;}
.preview-mode{background:#e3f2fd;border-left:4px solid #1976d2;padding:10px 15px;margin-bottom:15px;border-radius:6px;}
.execute-mode{background:#ffebee;border-left:4px solid #c62828;padding:10px 15px;margin-bottom:15px;border-radius:6px;}
.stats{background:#f0f4f8;padding:12px 15px;border-radius:8px;margin-top:15px;display:flex;gap:20px;flex-wrap:wrap;}
.stats-item{padding:5px 10px;border-radius:20px;background:#fff;}
</style></head><body>";

echo "<div class='toolbar'><h2>🖼️ 智能缩略图管理工具(增强版)</h2>";
echo "<p style='margin-top:-10px;color:#666;'>自动检测缩略图有效性 → 失效或为空时从正文提取第一张有效图片 → 正文无图则清空缩略图</p>";

if ($startid == 0 && $endid == 0) {
    echo '<form method="get" action="">
            <table style="border-collapse:collapse;">
            <tr><td style="padding:5px;">起始ID:</td><td style="padding:5px;"><input type="number" name="startid" value="1" required></td>
            <td style="padding:5px;">截止ID:</td><td style="padding:5px;"><input type="number" name="endid" value="5000" required></td></tr>
            <tr><td style="padding:5px;">每批处理:</td><td style="padding:5px;"><input type="number" name="step" value="50"></td>
            <td style="padding:5px;">超时(秒):</td><td style="padding:5px;"><input type="number" name="timeout" value="5"></td></tr>
            <tr><td style="padding:5px;">最小图片(字节):</td><td style="padding:5px;"><input type="number" name="min_size" value="256"></td>
            <td style="padding:5px;">模式:</td><td style="padding:5px;">
                <select name="mode">
                    <option value="preview">🔍 预览模式(仅报告,不修改)</option>
                    <option value="execute">⚙️ 执行模式(自动生成或清空)</option>
                </select>
            </td></tr>
            <tr><td colspan="4" style="padding:15px 5px;"><input type="submit" value="开始扫描处理" style="padding:10px 30px;"></td></tr>
            </table>
          </form>
          <p style="font-size:13px;color:#666;background:#f5f5f5;padding:10px;border-radius:6px;">
          📌 功能说明:<br>
          1. 检测文章缩略图(litpic)是否有效(HTTP状态码+Content-Type+图片大小验证)<br>
          2. 如果缩略图有效 → 保持不变<br>
          3. 如果缩略图失效或为空 → 从正文(body)中提取第一张有效的图片地址<br>
          4. 如果正文中有有效图片 → 设置为新的缩略图<br>
          5. 如果正文中没有有效图片 → 清空缩略图字段(删除失效地址)<br>
          6. 预览模式仅报告将要执行的操作,不会修改数据库
          </p>';
    echo "</div></body></html>";
    exit;
}

$mode_name = ($mode == 'execute') ? '执行模式(自动生成或清空)' : '预览模式(仅报告)';
$mode_class = ($mode == 'execute') ? 'execute-mode' : 'preview-mode';
echo "<div class='{$mode_class}'><strong>⚙️ 当前模式:{$mode_name}</strong><br>";
if ($mode == 'preview') {
    echo "📋 预览模式下仅扫描报告,不会修改数据库。确认无误后请切换为执行模式重新运行。";
} else {
    echo "⚠️ 执行模式将实际修改数据库,请确保已备份!";
}
echo "</div>";

echo "<p>📌 扫描范围: ID {$startid} ~ {$endid} &nbsp;|&nbsp; 每批处理: {$step} 篇 &nbsp;|&nbsp; 超时: {$timeout}秒 &nbsp;|&nbsp; 最小有效图片: {$min_size} 字节</p>";
echo "</div><div class='log'>";

// 查询文章列表
$sql = "SELECT a.id, a.title, a.litpic, b.body 
        FROM `dede_archives` a 
        LEFT JOIN `dede_addonarticle` b ON a.id = b.aid 
        WHERE a.id >= {$startid} AND a.id < (" . ($startid + $step) . ") AND a.id <= {$endid} 
        ORDER BY a.id ASC";
$dsql->SetQuery($sql);
$dsql->Execute();

$processed = 0;
$valid_count = 0;      // 缩略图有效
$invalid_count = 0;    // 缩略图失效
$empty_count = 0;      // 无缩略图
$generated_count = 0;  // 成功从正文生成缩略图
$cleared_count = 0;    // 清空缩略图(正文无图)
$failed_count = 0;     // 生成失败(正文无图且清空失败)
$last_id = $startid - 1;

echo "<h3 style='margin:0 0 10px 0;'>📋 处理日志</h3>";

while ($row = $dsql->GetArray()) {
    $id = $row['id'];
    $title = $row['title'];
    $litpic = trim($row['litpic']);
    $body = $row['body'];
    $last_id = $id;
    
    $processed++;
    
    // 状态标记
    $status = '';
    $new_litpic = '';
    $reason = '';
    
    // 1. 检测现有缩略图是否有效
    $current_valid = false;
    if (!empty($litpic)) {
        $check_result = check_image_valid($litpic, $timeout, $min_size);
        if ($check_result === true) {
            $current_valid = true;
            $valid_count++;
            $status = 'valid';
            echo "<div class='log-item'><span class='success'>✅ 有效</span> ID {$id} - 《" . htmlspecialchars($title) . "》<br>&nbsp;&nbsp;缩略图: " . htmlspecialchars($litpic) . "</div>";
            continue; // 缩略图有效,跳过处理
        } else {
            $invalid_count++;
            $reason = $check_result;
            $status = 'invalid';
            echo "<div class='log-item'><span class='error'>❌ 失效</span> ID {$id} - 《" . htmlspecialchars($title) . "》<br>&nbsp;&nbsp;原缩略图: " . htmlspecialchars($litpic) . "<br>&nbsp;&nbsp;失效原因: {$reason}</div>";
        }
    } else {
        $empty_count++;
        $status = 'empty';
        echo "<div class='log-item'><span class='warning'>📭 无缩略图</span> ID {$id} - 《" . htmlspecialchars($title) . "》</div>";
    }
    
    // 2. 缩略图失效或为空,尝试从正文提取有效图片
    if ($status == 'invalid' || $status == 'empty') {
        $extracted_url = extract_first_valid_image($body, $timeout, $min_size);
        
        if ($extracted_url !== false) {
            // 正文中有有效图片 → 设置为新的缩略图
            $generated_count++;
            echo "<div class='log-item'><span class='generated'>🖼️ 生成缩略图</span> ID {$id}<br>&nbsp;&nbsp;新缩略图: " . htmlspecialchars($extracted_url) . "</div>";
            
            if ($mode == 'execute') {
                $escaped_url = addslashes($extracted_url);
                $up_sql = "UPDATE `dede_archives` SET litpic='{$escaped_url}' WHERE id='{$id}'";
                $dsql->ExecuteNoneQuery($up_sql);
                echo "<div class='log-item' style='padding-left:20px;'><span class='success'>✅ 已更新数据库</span></div>";
            } else {
                echo "<div class='log-item' style='padding-left:20px;'><span class='info'>🔍 预览模式,未实际更新</span></div>";
            }
        } else {
            // 正文中没有有效图片 → 清空缩略图字段
            $cleared_count++;
            echo "<div class='log-item'><span class='cleared'>🗑️ 清空缩略图</span> ID {$id} - 正文中未找到有效图片,将清空缩略图字段</div>";
            
            if ($mode == 'execute') {
                $up_sql = "UPDATE `dede_archives` SET litpic='' WHERE id='{$id}'";
                $dsql->ExecuteNoneQuery($up_sql);
                echo "<div class='log-item' style='padding-left:20px;'><span class='success'>✅ 已清空数据库</span></div>";
            } else {
                echo "<div class='log-item' style='padding-left:20px;'><span class='info'>🔍 预览模式,未实际清空</span></div>";
            }
        }
    }
}

// 统计汇总
echo "<hr>";
echo "<div class='stats'>";
echo "<div class='stats-item'>📊 总计扫描: <strong>{$processed}</strong> 篇</div>";
echo "<div class='stats-item'>✅ 缩略图有效: <strong>{$valid_count}</strong> 篇</div>";
echo "<div class='stats-item'>❌ 缩略图失效: <strong>{$invalid_count}</strong> 篇</div>";
echo "<div class='stats-item'>📭 无缩略图: <strong>{$empty_count}</strong> 篇</div>";
echo "<div class='stats-item' style='background:#e8f0fe;'>🖼️ 从正文生成缩略图: <strong>{$generated_count}</strong> 篇</div>";
echo "<div class='stats-item' style='background:#fff3e0;'>🗑️ 清空缩略图(正文无图): <strong>{$cleared_count}</strong> 篇</div>";
echo "</div>";

if ($mode == 'preview' && ($invalid_count > 0 || $empty_count > 0)) {
    echo "<div class='info' style='margin-top:15px;padding:10px;background:#e3f2fd;border-radius:6px;'>💡 提示:当前为预览模式,未实际修改数据库。确认以上操作无误后,请切换为【执行模式】重新运行。</div>";
} elseif ($mode == 'execute') {
    echo "<div class='success' style='margin-top:15px;padding:10px;background:#e8f5e9;border-radius:6px;'>✅ 执行完成!共为 {$generated_count} 篇文章生成缩略图,清空 {$cleared_count} 篇失效缩略图。</div>";
}

// 自动分批继续
$next_start = $last_id + 1;
if ($next_start <= $endid && $processed > 0) {
    echo "<hr><div class='info'>⏳ 已完成 ID {$startid} ~ {$last_id},正在继续下一批...</div>";
    echo "<meta http-equiv='refresh' content='2;url=?startid={$next_start}&endid={$endid}&step={$step}&timeout={$timeout}&min_size={$min_size}&mode={$mode}'>";
} else {
    echo "<br><a href='?'>🔙 返回首页重新开始</a>";
}

/**
 * 从正文中提取第一张有效的图片地址
 * @param string $body 文章正文HTML
 * @param int $timeout 超时秒数
 * @param int $min_size 最小有效图片大小
 * @return string|false 图片地址或false
 */
function extract_first_valid_image($body, $timeout = 5, $min_size = 256) {
    if (empty($body)) return false;
    
    // 匹配所有img标签的src
    preg_match_all('/<img\s+[^>]*src=[\'"]([^\'"]+)[\'"][^>]*>/i', $body, $matches);
    
    if (empty($matches[1])) return false;
    
    foreach ($matches[1] as $url) {
        $url = trim($url);
        if (empty($url)) continue;
        
        // 验证图片有效性
        $check = check_image_valid($url, $timeout, $min_size);
        if ($check === true) {
            return $url;
        }
    }
    
    return false;
}

/**
 * 三重检测:状态码 + Content-Type + 真实图片格式/最小大小
 * @param string $url 图片地址
 * @param int $timeout 超时秒数
 * @param int $min_size 最小有效图片大小(字节)
 * @return bool|string true=有效, 其他=失效原因
 */
function check_image_valid($url, $timeout = 5, $min_size = 256) {
    $url = trim($url);
    if (empty($url)) return '空地址';
    
    // 本地文件检测
    if (strpos($url, 'http://') !== 0 && strpos($url, 'https://') !== 0) {
        $local_path = $_SERVER['DOCUMENT_ROOT'] . '/' . ltrim($url, '/');
        if (!file_exists($local_path)) return '本地文件不存在';
        $size = filesize($local_path);
        if ($size < $min_size) return "图片过小({$size}字节)";
        $info = @getimagesize($local_path);
        if ($info === false) return '本地文件非图片格式';
        return true;
    }
    
    // 远程URL检测
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_NOBODY, true);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_exec($ch);
    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $content_type = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
    $error = curl_errno($ch);
    curl_close($ch);
    
    if ($error !== 0) return "连接失败(error:{$error})";
    if ($http_code != 200) return "HTTP状态码:{$http_code}";
    if (strpos($content_type, 'image/') !== 0) return "非图片类型:{$content_type}";
    
    // 下载一小部分数据验证图片格式和大小
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_RANGE, "0-16383");
    $data = curl_exec($ch);
    $download_size = curl_getinfo($ch, CURLINFO_SIZE_DOWNLOAD);
    curl_close($ch);
    
    if ($data === false || $download_size == 0) return "无法获取图片数据";
    if ($download_size < $min_size) return "图片过小({$download_size}字节)";
    
    $info = @getimagesizefromstring($data);
    if ($info === false) return "非有效图片格式";
    
    return true;
}

echo "</div></body></html>";
?>

路过

鸡蛋

鲜花

握手

雷人

评论 (0 个评论)

facelist doodle 涂鸦板

您需要登录后才可以评论 登录 | 注册

Archiver|手机版|小黑屋|生物行[生物导航网] ( 沪ICP备05001519号 )

GMT+8, 2026-4-8 12:52 , Processed in 0.015994 second(s), 18 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

返回顶部